/**
 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "compiler/optimizer/ir/graph_visitor.h"
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/ir/graph_checker_macros.h"

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define CHECKER_DO_IF_NOT_VISITOR(visitor, cond, func) CHECKER_DO_IF_NOT_VISITOR_INTERNAL(visitor, InstChecker *, cond, func)

namespace ark::compiler {

class InstChecker : public GraphVisitor {
public:
    explicit InstChecker(const Graph *graph) : graph_(graph) {}

    const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
    {
        return graph_->GetBlocksRPO();
    }

    template<typename... Args>
    static bool CheckType(DataType::Type type, Args... types) {
        return ((types == type) || ...);
    }

    static DataType::Type GetInputType(Inst* inst, size_t input)
    {
        return inst->GetInput(input).GetInst()->GetType();
    }

    static bool Run(const Graph *graph)
    {
        auto checker = InstChecker(graph);
        checker.VisitGraph();
        return checker.GetStatus();
    }

    bool GetStatus() const
    {
        return success_;
    }

    void SetStatus(bool status)
    {
        success_ = status;
    }

    const Graph *GetGraph() const
    {
        return graph_;
    }

% IR::instructions.each do |inst|
    static void Visit<%= inst.opcode %>([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
    {
%   if !inst.has_dst?
        CHECKER_ASSERT(!inst->HasUsers());
%   elsif !inst.dst.pseudo?
        CHECKER_DO_IF_NOT_VISITOR(v, CheckType(inst->GetType(), <%= inst.dst.types.map { |x| Operand::cpp_type(x) }.join(', ') %>),
            std::cerr << "Wrong dst type '" << DataType::ToString(inst->GetType()) << "' for inst:\n" << *inst << std::endl);
%   end
%   if inst.inputs.any? { |x| x.is_dyn? }
    }
%     next
%   end
        CHECKER_EQ(inst->GetInputsCount(), <%= inst.inputs.size %>U);
%   inst.inputs.each_with_index do |operand, i|
%     if operand.has('save_state')
        CHECKER_ASSERT(inst->GetInput(<%= i %>).GetInst()->IsSaveState());
%     else
        CHECKER_DO_IF_NOT_VISITOR(v, CheckType(GetInputType(inst, <%= i %>), <%= operand.types.map { |x| Operand::cpp_type(x) }.join(', ') %>),
            std::cerr << "Wrong input <%= i %> type '" << DataType::ToString(GetInputType(inst, <%= i %>)) << "' for inst:\n" << *inst << std::endl);
%     end
%   end
    }
% end

#include "optimizer/ir/visitor.inc"

private:
    const Graph *graph_{nullptr};
    bool success_ {true};
};

#undef CHECKER_DO_IF_NOT_VISITOR

}  // namespace ark::compiler
